#include <wx/wxprec.h>

#ifndef WX_PRECOMP
    #include <wx/wx.h>
#endif

#include <wx/dcbuffer.h>
#include <wx/image.h>

#include "headers/main.h"

#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sstream>
#include <iostream>
#include <SDL/SDL.h>

#include "headers/FileLoader.h"

#include "headers/Windows.h"
#include "headers/PPUSystem.h"
#include "headers/LevelMenager.h"
#include "headers/Etc.h"

#include "headers/Callbacks.h"

#include "headers/Fonts.h"

FileLoader cFileLoader;
PPUSystem cPPUSystem;
LevelMenager cLevelMenager;
WindowsMenager cWindowsMenager;
SquaresList cSquaresList(10);

#include "headers/Enemies.h"
Enemies cEnemies;


SDL_Surface* screen = NULL;

const char WindowTitle[] = "DizzyEdit ver. alpha alpha alpha";


int InitAll();
static void draw();

int mx, my;

int GetMousePosition(int* x, int* y) {  
    *x = mx;
    *y = my;
}

int LeftDown;

/*******************************************************************************
// SDLPanel Class
*******************************************************************************/


IMPLEMENT_CLASS(SDLPanel, wxPanel)

BEGIN_EVENT_TABLE(SDLPanel, wxPanel)
    EVT_MOUSE_EVENTS(SDLPanel::onMouse)
    EVT_KEY_DOWN(SDLPanel::onKey)
    EVT_PAINT(SDLPanel::onPaint)
    EVT_ERASE_BACKGROUND(SDLPanel::onEraseBackground)
    EVT_IDLE(SDLPanel::onIdle)
END_EVENT_TABLE()


SDLPanel::SDLPanel(wxWindow *parent) : wxPanel(parent, ID_PANEL) {
    // ensure the size of the wxPanel
    wxSize size(640, 460);
    
    SetMinSize(size);
    SetMaxSize(size);
}

SDLPanel::~SDLPanel() {
    if (screen != NULL) {
        SDL_FreeSurface(screen);
    }
}

inline void SDLPanel::onEraseBackground(wxEraseEvent &) {}

void SDLPanel::onMouse(wxMouseEvent& event) {
    
    if (event.ButtonDown(wxMOUSE_BTN_LEFT)) {
        int x = event.m_x;
        int y = event.m_y;  
        
        LeftDown = 1;
        
        cWindowsMenager.MouseClick(x, y, 0);
    }
    
    if (event.ButtonUp(wxMOUSE_BTN_LEFT)) {
        LeftDown = 0;
        cWindowsMenager.MouseUp();
    }
    
    mx = event.m_x;
    my = event.m_y;
    
}


void SDLPanel::onKey(wxKeyEvent& event) {
    
    switch (event.m_keyCode) {
        case WXK_LEFT: {
            int lvl = cLevelMenager.GetCurrentLvl()-1;
            if (lvl<0) break;
            if (lvl==0x10) lvl=0xf;                             
            cLevelMenager.LoadLevel(lvl); break;
        }
        case WXK_RIGHT: {
            int lvl = cLevelMenager.GetCurrentLvl()+1;
            if (lvl>0x31) break;
            if (lvl==0x10) lvl=0x11;
            cLevelMenager.LoadLevel(lvl); break;            
        }
    }

}


void SDLPanel::onPaint(wxPaintEvent &) {

    // can't draw if the screen doesn't exist yet
    if (screen == NULL) {
        return;
    }
    
    // lock the surface if necessary
    if (SDL_MUSTLOCK(screen)) {
        if (SDL_LockSurface(screen) < 0) {
            return;
        }
    }
    
    // create a bitmap from our pixel data
    wxBitmap bmp(wxImage(screen->w, screen->h, 
                    static_cast<unsigned char *>(screen->pixels), true), 24);
    
    // unlock the screen
    if (SDL_MUSTLOCK(screen)) {
        SDL_UnlockSurface(screen);
    }
    
    // paint the screen
    wxBufferedPaintDC dc(this, bmp);
   
}

void SDLPanel::onIdle(wxIdleEvent &) {

    // Lock surface if needed
    if (SDL_MUSTLOCK(screen)) {
        if (SDL_LockSurface(screen) < 0) {
            return;
        }
    }
    
    draw();
        
    // Unlock if needed
    if (SDL_MUSTLOCK(screen)) {
        SDL_UnlockSurface(screen);
    }
    
    // refresh the panel
    Refresh(false);
     
}


/************************************
*********** SDLFrame class **********
************************************/

IMPLEMENT_CLASS(SDLFrame, wxFrame)

BEGIN_EVENT_TABLE(SDLFrame, wxFrame)
    EVT_MENU(IDM_FILE_OPEN, SDLFrame::onFileOpen)
    EVT_MENU(IDM_FILE_SAVE, SDLFrame::onFileSave)
    EVT_MENU(IDM_FILE_EXIT, SDLFrame::onFileExit)
    EVT_MENU(IDM_HELP_HELP, SDLFrame::onHelpHelp)
    EVT_MENU(IDM_HELP_ABOUT, SDLFrame::onHelpAbout)
END_EVENT_TABLE()


void SDLFrame::onFileOpen(wxCommandEvent &) { 
     
    wxString filename = wxFileSelector("Choose a file to open",
                        "","","","NES files (*.nes)|*.nes|All files|*.*", wxOPEN);
    
    if (filename.empty()) return;
    
    RomOk();
        
    if (cFileLoader.LoadFile(filename.c_str())) { 
        SomethingWrong();
        return;
    }
        
    cLevelMenager.LoadLevel(0);
    
}


void SDLFrame::onFileSave(wxCommandEvent &) { 
    
    if (!IsRomOk()) return;
    
    wxString filename = wxFileSelector("Save As",
                        "","*.nes","","NES files (*.nes)|*.nes|All files|*.*", wxSAVE);
    
    if (filename.empty()) return;
            
    cFileLoader.SaveToFile(filename.c_str());

}

inline void SDLFrame::onFileExit(wxCommandEvent &) { Close(); }
inline SDLPanel &SDLFrame::getPanel() { return *panel; }

SDLFrame::SDLFrame() {
    // Create the SDLFrame
    Create(NULL, ID_FRAME, wxT(WindowTitle), wxDefaultPosition,
           wxDefaultSize, wxCAPTION | wxSYSTEM_MENU | 
           wxMINIMIZE_BOX | wxCLOSE_BOX);

    // create the main menubar
    wxMenuBar *mb = new wxMenuBar;
    
    // create the file menu
    wxMenu *fileMenu = new wxMenu;
    fileMenu->Append(IDM_FILE_OPEN, wxT("&Open"));
    fileMenu->Append(IDM_FILE_SAVE, wxT("&Save as"));
    fileMenu->AppendSeparator();
    fileMenu->Append(IDM_FILE_EXIT, wxT("E&xit"));
    
    // add the file menu to the menu bar
    mb->Append(fileMenu, wxT("&File"));
    
    // create the help menu
    wxMenu *helpMenu = new wxMenu;
    helpMenu->Append(IDM_HELP_HELP, wxT("Pomocy!"));
    helpMenu->AppendSeparator();
    helpMenu->Append(IDM_HELP_ABOUT, wxT("About"));
    
    // add the help menu to the menu bar
    mb->Append(helpMenu, wxT("&Help"));
    
    // add the menu bar to the SDLFrame
    SetMenuBar(mb);
    
    // create the SDLPanel
    panel = new SDLPanel(this);
}

void SDLFrame::onHelpHelp(wxCommandEvent &) {

    wxMessageBox(wxT("Plansze zmienia się kursorami (prawo, lewo)."),
                 wxT("Help"), wxOK | wxICON_INFORMATION);

}

void SDLFrame::onHelpAbout(wxCommandEvent &) {
    wxMessageBox(wxT("DizzyEdit\nby Grimm\n2006\n\nWszelkie błędy, etc. zgłaszać na:\nb_grimm@wp.pl"),
                 wxT("about DizzyEdit"), wxOK | wxICON_INFORMATION);
}


/*******************************************************************************
// SDLApp Class
*******************************************************************************/


bool SDLApp::OnInit() {
    // create the SDLFrame
    frame = new SDLFrame;
    frame->SetClientSize(640, 460);
    frame->Centre();
    frame->Show();
    
    // Our SDLFrame is the Top Window
    SetTopWindow(frame);

    // initialization should always succeed
    return true;
}

int SDLApp::OnRun() {
    // initialize SDL
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        std::cerr << "SDL Error: " << SDL_GetError() << '\n';
        
        return -1;
    }
    
    // Setup video mode, but don't create a window
    SDL_SetVideoMode(0, 0, 0, SDL_SWSURFACE);
    
    screen = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 460, 
                                      24, 0xff, 0xff00, 0xff0000, 0); 
    
    // generate an initial idle event to start things
    wxIdleEvent event;
    event.SetEventObject(&frame->getPanel());
    frame->getPanel().AddPendingEvent(event);
    
    InitText();
    InitAll();

    // start the main loop
    return wxApp::OnRun();
}

int SDLApp::OnExit() {
    // cleanup SDL
    SDL_Quit();
    
    // return the standard exit code
    return wxApp::OnExit();
}


IMPLEMENT_CLASS(SDLApp, wxApp)
IMPLEMENT_APP(SDLApp)


int InitAll() {
    
    
    cFileLoader.Init();
    cPPUSystem.Init();
        
    /* Level */    
    cWindowsMenager.AddWindow("Level", ALL_FRAMES, 380, 192, MouseClickLevelMenager);
    cWindowsMenager.SetDestX("Level", 16);
    cWindowsMenager.SetDestY("Level", 16);
    
    /* Squares List : */
    cWindowsMenager.AddWindow("SquaresList", ALL_FRAMES, 338, 1, MouseClickSquaresList);
    cWindowsMenager.SetDestX("SquaresList", 16);
    cWindowsMenager.SetDestY("SquaresList", 232);
    cWindowsMenager.SetDestH("SquaresList", 200);
    
    
    cLevelMenager.SetDrawObjectName("Level");
    cEnemies.SetWindowName("Level");
       
}   

static void draw() {
    
    while (IsRomOk()) {    
        cEnemies.Draw(); if (!IsRomOk()) break;
        cSquaresList.DrawFrame("SquaresList"); if (!IsRomOk()) break;
        cLevelMenager.DrawFrame(); if (!IsRomOk()) break;
        
        cWindowsMenager.DrawAll();
        
        break;     
    }

    //SDL_Flip (screen);
    // throttle to keep from flooding the event queue
    wxMilliSleep(20);
    
}

